package com.pubinfo.passbook.common.service.request.tax.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.mychain.gov.sdk.GovDataSDK;
import com.alipay.mychain.gov.sdk.base.*;
import com.alipay.mychain.gov.sdk.base.enums.InvoiceTypeEnum;
import com.alipay.mychain.gov.sdk.base.model.BaseInvoiceContent;
import com.alipay.mychain.gov.sdk.facade.enums.EncryptTpyeEnum;
import com.alipay.mychain.gov.sdk.request.SignedRequest;
import com.pubinfo.passbook.common.config.ApplicationConfig;
import com.pubinfo.passbook.common.entity.*;
import com.pubinfo.passbook.common.exception.ServiceException;
import com.pubinfo.passbook.common.mapper.TSourceInvoiceMapper;
import com.pubinfo.passbook.common.mapper.TSubsidiaryPrivateKeyMapper;
import com.pubinfo.passbook.common.mapper.TSystemTaskLogMapper;
import com.pubinfo.passbook.common.model.bo.SubsidiaryInfo;
import com.pubinfo.passbook.common.model.constant.LogNameEnum;
import com.pubinfo.passbook.common.model.dto.tax.QueryByTimeResult;
import com.pubinfo.passbook.common.service.master.*;
import com.pubinfo.passbook.common.service.request.tax.RequestTaxServer;
import com.pubinfo.passbook.common.utils.GetPhoneNumber;
import com.pubinfo.passbook.common.utils.tax.Util;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RequestTaxServerImpl implements RequestTaxServer {
  
  @Resource
  private ApplicationConfig config;
  @Autowired
  private TSyncLogService kafkaSender;
  
  @Autowired
  TSystemTaskLogMapper taskLogMapper;
  @Autowired
  private TSubsidiaryPrivateKeyMapper keyMapper;
  @Resource
  TSourceCarInvoiceService carInvoiceService;
  @Resource
  TSourceNormalService normalService;
  @Resource
  TSourceSecondHandCarInvoiceService handCarInvoiceService;
  @Resource
  TSourceInvoiceItemService itemService;
  
  @Autowired
  TInvoiceService invoiceService;
  @Autowired
  TSourceInvoiceMapper tSourceInvoiceMapper;
  
  @Override
  public QueryByTimeResult queryByTime(String requestId, String kprq, SubsidiaryInfo info) {
    String nsrsbh = info.getNsrsbh();
    // String kprq = "2021-06-20";
    TRequestLog syncLog = new TRequestLog();
    String gx_falg = "1";
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("authorizedNsrsbh", nsrsbh);
    jsonObject.put("nsrsbh", nsrsbh);
    //  jsonObject.put("kprq", kprq);
    jsonObject.put("jgrq", kprq);
  
    jsonObject.put("gxfbs", gx_falg);
    jsonObject.put("requestId", requestId);
    String jsonStr = JSONObject.toJSONString(jsonObject);
    String signStr = sign(jsonStr, info);
    String url = config.getTax().getApi().getQueryByTime();
    syncLog.setType(1).setParam(jsonObject.toJSONString()).setUrl(url).setStatus(2).setName(LogNameEnum.QUERY_BY_TIME.getCode());
  
    QueryByTimeResult timeResult = post(signStr, config.getTax().getUrl() + url, syncLog, 0, info);
  
    //   kafkaSender.save(syncLog);
    return timeResult;
  }
  
  @Override
  public List<String> check(String id) {
    List<SubsidiaryInfo> infoList = keyMapper.listTaxAllSubsidiaryInfo(id);
    String startDate = Util.getTaxOnTheDay();
    List<String> names = new ArrayList<>();
    if (!CollectionUtils.isEmpty(infoList)) {
      for (SubsidiaryInfo info : infoList) {
        UUID uuid = UUID.randomUUID();
        QueryByTimeResult result = queryByTime(uuid.toString(), startDate, info);
        if (result == null || !result.getSuccess()) {
          names.add(info.getName());
        }
      }
    }
    
    return names;
  }
  
  @SneakyThrows
  @Override
  public Integer sync(String startDate, String endDate) {
    List<String> dateList = Util.getTaxTwoDaysDay(startDate, endDate);
    TTaskLog systemTaskLog = new TTaskLog();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date start = sdf.parse(startDate);
    Date end = sdf.parse(endDate);
    systemTaskLog.setStartDate(start);
    systemTaskLog.setEndDate(end);
    Integer totalCount = 0;
    if (!CollectionUtils.isEmpty(dateList)) {
      for (String date : dateList) {
        totalCount += saveByDateAndTaskId(date, null);
      }
    }
    systemTaskLog.setName("all");
    systemTaskLog.setAcceptTotalCount(totalCount);
    systemTaskLog.setTaskId("1");
    
    taskLogMapper.insert(systemTaskLog);
    log.info("总批量入库:" + totalCount + "条数据");
    return totalCount;
  }
  
  @SneakyThrows
  @Override
  public Integer saveByDate(String date) {
    return saveByDateAndTaskId(date, null);
  }

//  @Override
//  public QueryByTimeResult queryByTime(JSONObject json) {
//
//    String jsonStr = JSONObject.toJSONString(json);
//    //  List<SubsidiaryInfo> infoList = keyMapper.listTaxAllSubsidiaryInfo(json.getString("nsrsbh"));
//    List<String> objectList = redisService.getList("subsidiary-info");
//    List<SubsidiaryInfo> infoList = new ArrayList<>();
//    if (CollectionUtils.isEmpty(objectList)) {
//      return null;
//    }
//    objectList.stream().forEach(str -> {
//      infoList.add(JSONObject.parseObject(str, SubsidiaryInfo.class));
//    });
//    String signStr = sign(jsonStr, infoList.get(0));
//    String url = config.getTax().getApi().getQueryByTime();
//
//    QueryByTimeResult timeResult = post(signStr, config.getTax().getUrl() + url, new TRequestLog(), 0, infoList.get(0));
//
//    return timeResult;
//  }
  @Override
  public List<TSourceInvoice> queryByDateAndInfo(String info, String date) {
    List<SubsidiaryInfo> subsidiaryInfos = keyMapper.listTaxAllSubsidiaryInfo(info);
    return listByDate(subsidiaryInfos.get(0), date);
  }
  public QueryByTimeResult post(String jsonStr, String uri, TRequestLog syncLog, Integer count, SubsidiaryInfo info) {
    String response = null;
    try {
      response = HttpClientUtils.doPost(uri, jsonStr, ContentType.APPLICATION_JSON);
    } catch (Exception e) {
      log.error("访问省税务接口失败!", e);
    }
    QueryByTimeResult pageResult = JSON.parseObject(response, QueryByTimeResult.class);
    //先保存日志 重试三次
    Boolean success = null;
    if (StringUtils.isEmpty(pageResult)) {
      count++;
      if (count <= 3) {
        post(jsonStr, uri, syncLog, count, info);
      } else {
        syncLog.setStatus(0);
        log.error(syncLog.getName() + "失败:数据:" + pageResult);
      }
    } else {
      success = pageResult.getSuccess();
      syncLog.setData(JSONObject.toJSONString(pageResult));
      if (!success) {
        String msg = pageResult.getErrorMsg();
        syncLog.setStatus(0);
        log.error(syncLog.getName() + "请求数据" + JSONObject.toJSON(syncLog.getParam()) + "认证数据" + JSONObject.toJSON(info) + "失败:" + msg + " 数据:" + JSONObject.toJSON(pageResult));
        kafkaSender.save(syncLog);
      }
    }
    
    return pageResult;
  }
  
  @SneakyThrows
  @Override
  public List<TSourceInvoice> fetchPage(String requestId, Integer pageNumber, SubsidiaryInfo info, String date) {
    
    TRequestLog syncLog = new TRequestLog();
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("nsrsbh", info.getNsrsbh());
    jsonObject.put("pageNumber", pageNumber);
    jsonObject.put("requestId", requestId);
    String jsonStr = JSONObject.toJSONString(jsonObject);
    String url = config.getTax().getApi().getFetchPage();
    
    syncLog.setType(1).setParam(jsonObject.toJSONString()).setUrl(url).setStatus(2).setName(LogNameEnum.FETCH_PAGE.getCode());
    
    PageResult pageResult = postAndDecrypt(sign(jsonStr, info), config.getTax().getUrl() + url, syncLog, 0);
    DecryptAndVerifyUtils decryptAndVerifyUtils = new DecryptAndVerifyUtils(info.getNsrsbh(), info.getPassword(), info.getPrivateKey());
    
    List<TrustedInvoiceContent> encryptedDataAndSignData = decryptAndVerifyUtils.getEncryptedDataAndSignData(pageResult);
    // 证书验证 & 明文操作
    List<TSourceInvoice> invoiceList = new ArrayList<>();
    if (!CollectionUtils.isEmpty(encryptedDataAndSignData)) {
      for (TrustedInvoiceContent encryptedDataAndSignDatum : encryptedDataAndSignData) {
        TSourceInvoice invoice = toTInvoice(encryptedDataAndSignDatum, decryptAndVerifyUtils);
        invoice.setNsrsbhName(info.getName());
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        invoice.setJgrq(format.parse(date));
        invoiceList.add(invoice);
        //       // invoiceSender.sendInvoices(invoice);
      }
    }
    kafkaSender.save(syncLog);
    //saveOrUpdateBatch()
    return invoiceList;
  }
  
  @Override
  @SneakyThrows
  public List<TSourceInvoice> listByDate(SubsidiaryInfo info, String date) {
    TTaskLog systemTaskLog = new TTaskLog();
    List<TSourceInvoice> invoiceListAll = new ArrayList<>();
    UUID uuid = UUID.randomUUID();
    QueryByTimeResult result = queryByTime(uuid.toString(), date, info);
    Integer successCount = 0;
    List<TSourceInvoice> sourceInvoices = new ArrayList<>();
    if (result != null && result.getSuccess()) {
      Integer totalPage = result.getData().getTotalPage();
      Long startTime = System.currentTimeMillis();
      
      if (totalPage > 0) {
        for (int i = 1; i <= totalPage; i++) {
          List<TSourceInvoice> invoiceList = fetchPage(uuid.toString(), i, info, date);
          if (!CollectionUtils.isEmpty(invoiceList)) {
            successCount += invoiceList.size();
          }
          sourceInvoices.addAll(invoiceList);
        }
      }
      Long endTime = System.currentTimeMillis();
      Integer totalCount = result.getData().getTotalCount();
      systemTaskLog.setStartDate(new SimpleDateFormat("yyyy-MM-dd").parse(date));
      systemTaskLog.setAcceptTotalCount(totalCount);
      systemTaskLog.setSuccessCount(successCount);
      systemTaskLog.setTaskId("1");
      systemTaskLog.setName(info.getName());
      systemTaskLog.setStatus(successCount == totalCount ? 0 : 1);
      systemTaskLog.setResponseTime(endTime - startTime);
      // systemTaskLog.setId(IdWorker.get32UUID());
      taskLogMapper.insert(systemTaskLog);
      //taskLogService.sendTaskLogSave(systemTaskLog);
      sourceInvoices.stream().forEach(tSourceInvoice -> {
        tSourceInvoice.setLogId(systemTaskLog.getId());
      });
      invoiceListAll.addAll(sourceInvoices);
      log.info(date + "日" + info.getName() + ",总共" + totalCount + "条数据");
    }
    
    return invoiceListAll;
  }
  
  @Override
  public Integer saveByDateAndTaskId(String date, String taskId) {
    List<TSourceInvoice> tSourceInvoices=tSourceInvoiceMapper.selectInvoiceByJgrq(date);
    List<Integer> dbList = tSourceInvoices.stream().map(TSourceInvoice::getFpdmAndFphmHash).collect(Collectors.toList());
    List<SubsidiaryInfo> infoList = keyMapper.listTaxAllSubsidiaryInfo(null);
    //一次性提交保存
    Integer count = 0;
    List<TSourceInvoice> invoiceListAll = new ArrayList<>();
    if (!CollectionUtils.isEmpty(infoList)) {
      Long startDate = System.currentTimeMillis();
      for (SubsidiaryInfo info : infoList) {
        //如果有3万条,则先保存,再查询添加
        if (invoiceListAll.size() > 30000) {
          Long sqlStartDate = System.currentTimeMillis();
          invoiceService.saveBatch(invoiceListAll);
          saveSourceInvoice(invoiceListAll);
          count += invoiceListAll.size();
          log.info("sql执行时间" + (System.currentTimeMillis() - sqlStartDate) + "ms");
          invoiceListAll.clear();
        }

        List<TSourceInvoice> invoiceList=listByDate(info, date);
        List<Integer> sourceList = invoiceList.stream().map(TSourceInvoice::getFpdmAndFphmHash).collect(Collectors.toList());
        sourceList.removeAll(dbList);
        invoiceList = invoiceList.stream().filter(e -> sourceList.contains(e.getFpdmAndFphmHash())).collect(Collectors.toList());
        invoiceListAll.addAll(invoiceList);
  
      }
      log.info("数据获取时间" + (System.currentTimeMillis() - startDate) + "ms");
    }
  
    Long startDate = System.currentTimeMillis();
    invoiceService.saveBatch(invoiceListAll);
    saveSourceInvoice(invoiceListAll);
    log.info("sql执行时间" + (System.currentTimeMillis() - startDate) + "ms");
    count += invoiceListAll.size();
    return count;
  }
  
  private void saveSourceInvoice(List<TSourceInvoice> invoiceList) {
    long start = System.currentTimeMillis();
    List<TSourceCarInvoice> sourceCarInvoiceList = new ArrayList<>();
    List<TSourceSecondHandCarInvoice> sourceSecondHandCarInvoiceList = new ArrayList<>();
    List<TSourceNormal> sourceNormalList = new ArrayList<>();
    List<TSourceInvoiceItem> sourceInvoiceItemList = new ArrayList<>();
    for (TSourceInvoice sourceInvoice : invoiceList) {
      InvoiceTypeEnum typeEnum = InvoiceTypeEnum.getByCode(sourceInvoice.getFplx());
      JSONObject json = sourceInvoice.getDataJson();
      switch (typeEnum) {
        //机动车
        case JDCFP:
          //转换
          TSourceCarInvoice carInvoice = JSONObject.toJavaObject(json, TSourceCarInvoice.class);
          carInvoice.setId(sourceInvoice.getId());
          List<TSourceInvoiceItem> carInvoiceItems = carInvoice.getItems();
          if (!CollectionUtils.isEmpty(carInvoiceItems)) {
            carInvoiceItems.stream().forEach(item -> {
              item.setParentId(sourceInvoice.getId());
            });
          }
          sourceCarInvoiceList.add(carInvoice);
          if (!CollectionUtils.isEmpty(carInvoiceItems)) {
            sourceInvoiceItemList.addAll(carInvoiceItems);
          }
          break;
        //二手车
        case ESCFP:
          //转换
          TSourceSecondHandCarInvoice handCarInvoice = JSONObject.toJavaObject(json, TSourceSecondHandCarInvoice.class);
          handCarInvoice.setId(sourceInvoice.getId());
          List<TSourceInvoiceItem> handCarInvoiceItems = handCarInvoice.getItems();
          if (!CollectionUtils.isEmpty(handCarInvoiceItems)) {
            handCarInvoiceItems.stream().forEach(item -> {
              item.setParentId(sourceInvoice.getId());
            });
          }
          sourceSecondHandCarInvoiceList.add(handCarInvoice);
          if (!CollectionUtils.isEmpty(handCarInvoiceItems)) {
            sourceInvoiceItemList.addAll(handCarInvoiceItems);
          }
          break;
        //普通发票
        default:
          //转换
          TSourceNormal sourceNormal = JSONObject.toJavaObject(json, TSourceNormal.class);
          sourceNormal.setId(sourceInvoice.getId());
          List<TSourceInvoiceItem> normalItems = sourceNormal.getItems();
          if (!CollectionUtils.isEmpty(normalItems)) {
            normalItems.stream().forEach(item -> {
              item.setParentId(sourceInvoice.getId());
            });
          }
          sourceNormal.setGmfdh(GetPhoneNumber.getPhoneFromStr(sourceNormal.getGmfdzdh()));
          sourceNormalList.add(sourceNormal);
          if (!CollectionUtils.isEmpty(normalItems)) {
            sourceInvoiceItemList.addAll(normalItems);
          }
      }
    }
    saveCarInvoice(sourceCarInvoiceList);
    saveSecondHandCarInvoice(sourceSecondHandCarInvoiceList);
    saveNormal(sourceNormalList);
    saveInvoiceItem(sourceInvoiceItemList);
  }
  
  private void saveCarInvoice(List<TSourceCarInvoice> sourceCarInvoiceList) {
    if (CollectionUtils.isEmpty(sourceCarInvoiceList)) {
      return;
    }
    for (int i = 0; i < sourceCarInvoiceList.size(); i += 2000) {
      carInvoiceService.insertList(sourceCarInvoiceList.subList(i, Math.min(i + 2000, sourceCarInvoiceList.size())));
    }
  }
  
  private void saveSecondHandCarInvoice(List<TSourceSecondHandCarInvoice> sourceSecondHandCarInvoiceList) {
    if (CollectionUtils.isEmpty(sourceSecondHandCarInvoiceList)) {
      return;
    }
    for (int i = 0; i < sourceSecondHandCarInvoiceList.size(); i += 2000) {
      handCarInvoiceService.insertList(sourceSecondHandCarInvoiceList.subList(i, Math.min(i + 2000, sourceSecondHandCarInvoiceList.size())));
    }
  }
  
  private void saveNormal(List<TSourceNormal> sourceNormalList) {
    if (CollectionUtils.isEmpty(sourceNormalList)) {
      return;
    }
    for (int i = 0; i < sourceNormalList.size(); i += 2000) {
      normalService.insertList(sourceNormalList.subList(i, Math.min(i + 2000, sourceNormalList.size())));
    }
  }
  
  private void saveInvoiceItem(List<TSourceInvoiceItem> sourceInvoiceItemList) {
    if (CollectionUtils.isEmpty(sourceInvoiceItemList)) {
      return;
    }
    for (int i = 0; i < sourceInvoiceItemList.size(); i += 2000) {
      itemService.insertList(sourceInvoiceItemList.subList(i, Math.min(i + 2000, sourceInvoiceItemList.size())));
    }
  }
  
  @Override
  public void sync(String taskId) {
    //取上一天的日期
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Calendar calendar = Calendar.getInstance();
    saveByDateAndTaskId(sdf.format(calendar.getTime()), taskId);
  }
  
  public String sign(String jsonStr, SubsidiaryInfo info) {
    
    GovDataSDK.getInstance().init(info.getNsrsbh(), info.getPrivateKey(), info.getPassword(), EncryptTpyeEnum.SM4);
    SignedRequest request1 = null;
    try {
      request1 = GovDataSDK.getInstance().getRequestBuilder().generateSignedPayload(jsonStr);
    } catch (Exception e) {
      //log.info("数据:"+info+"异常"+e);
      throw new ServiceException("数据:" + info + "异常" + e);
    }
    
    return JSON.toJSONString(request1);
  }
  
  public PageResult postAndDecrypt(String jsonStr, String uri, TRequestLog syncLog, Integer count) {
    // 获取密文和签名集合
    String response = null;
    
    try {
      
      response = HttpClientUtils.doPost(uri, jsonStr, ContentType.APPLICATION_JSON);
      
    } catch (Exception e) {
      e.printStackTrace();
    }
    
    GenericResult result = JSON.parseObject(response, GenericResult.class);
    if (StringUtils.isEmpty(result)) {
      count++;
      if (count <= 3) {
        postAndDecrypt(jsonStr, uri, syncLog, count);
      } else {
        syncLog.setStatus(0);
        log.error(syncLog.getName() + "失败:数据:" + result);
      }
    }
    // System.out.println(result);
    PageResult pageResult = JSONObject.parseObject(result.getData(), PageResult.class);
    // logService.save(syncLog);
    
    return pageResult;
  }
  
  private TSourceInvoice toTInvoice(TrustedInvoiceContent encryptedDataAndSignDatum, DecryptAndVerifyUtils decryptAndVerifyUtils) {
    BaseInvoiceContent baseInvoiceContent = decryptAndVerifyUtils.parse(new String(encryptedDataAndSignDatum.getInvoiceContent(), StandardCharsets.UTF_8));
    TSourceInvoice invoice = new TSourceInvoice(baseInvoiceContent, encryptedDataAndSignDatum);
    InvoiceTypeEnum typeEnum = InvoiceTypeEnum.getByCode(invoice.getFplx());
    JSONObject json = invoice.getDataJson();
    switch (typeEnum) {
      //机动车
      case JDCFP:
      case ESCFP:
        break;
      //普通发票
      default:
        invoice.setJe(json.getBigDecimal("jE"));
        invoice.setJshj(json.getBigDecimal("jSHJ"));
        invoice.setSe(json.getBigDecimal("sE"));
    }
    boolean verify = decryptAndVerifyUtils.verify(encryptedDataAndSignDatum.getTrustedData());
    if (verify) {
      invoice.setStatus(0);
    } else {
      invoice.setStatus(1);
    }
    return invoice;
  }
  
}



